﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Lookstone.Dates
{
	public static class Convert
	{
		/// <summary>
		/// Converts the date to a string in YYYYMMDD format
		/// </summary>
		/// <param name="date">Date to convert</param>
		/// <returns>String containing the formatted date</returns>
		public static string YearMonthDayString(DateTime date)
		{
			return date.ToString("yyyyMMdd");
		}

		/// <summary>
		/// Converts a date string in YYYYMMDD format to a DateTime value
		/// </summary>
		/// <param name="dateString">String in YYYYMMDD format</param>
		/// <returns>DateTime containing the value parsed from the passed in string</returns>
		public static DateTime DateFromYearMonthDayString(string dateString)
		{
			try
			{
				if (dateString == null || dateString.Length == 0)
					return DateTime.MinValue;

				return DateTime.ParseExact(dateString, "yyyyMMdd", null);
			}
			catch
			{
				return DateTime.MinValue;
			}
		}

		/// <summary>
		/// Retrieves the first day of the month for the passed in date
		/// </summary>
		/// <param name="date">Date to retrieve the first day of the month</param>
		/// <returns>DateTime value containing the first day of the month</returns>
		public static DateTime FirstDayOfMonth(this DateTime date)
		{
			DateTime retVal;

			retVal = new DateTime(date.Year, date.Month, 1);
			return retVal;
		}

		/// <summary>
		/// Retrieves the last day of the month for the passed in date
		/// </summary>
		/// <param name="date">Date to retrieve the last day of the month</param>
		/// <returns>DateTime value containing the last day of the month</returns>
		public static DateTime LastDayOfMonth(this DateTime date)
		{
			DateTime retVal;

			retVal = FirstDayOfMonth(date);
			retVal = retVal.AddMonths(1);
			retVal = retVal.AddDays(-1);
			return retVal;
		}

		public static DateTime FirstDayOfYear(this DateTime date)
		{
			DateTime retVal;

			retVal = new DateTime(date.Year, 1, 1);
			return retVal;
		}

		public static DateTime FirstDayOfYear(int year)
		{
			DateTime retVal;

			retVal = new DateTime(year, 1, 1);
			return retVal;
		}

		public static DateTime LastDayOfYear(this DateTime date)
		{
			DateTime retVal;

			retVal = new DateTime(date.Year, 12, 31);
			return retVal;
		}

		public static DateTime LastDayOfYear(int year)
		{
			DateTime retVal;

			retVal = new DateTime(year, 12, 31);
			return retVal;
		}

		public static string DateToString(DateTime date)
		{
			return date.ToString("MM/dd/yyyy");
		}

		public static DateTime GetMostRecent(DateTime firstDate, DateTime secondDate)
		{
			return firstDate > secondDate ? firstDate : secondDate;
		}

		/// <summary>
		/// Generates an inclusive array of DateTime objects for each date between the first date and last date
		/// </summary>
		/// <param name="firstDate">First date of the series</param>
		/// <param name="lastDate">Last date of the series</param>
		/// <returns>Array of DateTime objects for the range</returns>
		public static DateTime[] CreateRange(this DateTime firstDate, DateTime lastDate)
		{
			List<DateTime> dates = new List<DateTime>();

			for (DateTime current = firstDate; current <= lastDate; current = current.AddDays(1))
				dates.Add(current);

			return dates.ToArray();
		}

		/// <summary>
		/// Determines if the date is contained within the range or not
		/// </summary>
		/// <param name="checkDate">Date to be checked</param>
		/// <param name="date1">First date of the date range</param>
		/// <param name="date2">Last date of the date range</param>
		/// <returns>True if checkDate is contained in the date range described by date1 and date2</returns>
		public static bool IsInRange(this DateTime checkDate, DateTime date1, DateTime date2)
		{
			// First check to see if the passed in dates are in order. If so, then check to see if checkDate is between them
			if (date1 <= date2)
				return checkDate >= date1 && checkDate <= date2;

			// Otherwise invert them and check the checkDate to see if it is between them
			return checkDate >= date2 && checkDate <= date1;
		}

		/// <summary>
		/// Determines if the date is contained within the range or not
		/// </summary>
		/// <param name="checkDate">Date to be checked. If null, then returns false.</param>
		/// <param name="date1">First date of the date range</param>
		/// <param name="date2">Last date of the date range</param>
		/// <returns>True if checkDate is contained in the date range described by date1 and date2</returns>
		public static bool IsInRange(this DateTime? checkDate, DateTime date1, DateTime date2)
		{
			if (checkDate == null)
				return false;

			return IsInRange(checkDate.Value, date1, date2);
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SYSTEMTIME
		{
			public ushort wYear;
			public ushort wMonth;
			public ushort wDayOfWeek;
			public ushort wDay;
			public ushort wHour;
			public ushort wMinute;
			public ushort wSecond;
			public ushort wMilliseconds;
		}

		[DllImport("kernel32.dll")]
		private static extern int SetSystemTime(ref SYSTEMTIME lpSysTime);

		/// <summary>
		/// Sets the local system time to the time specified
		/// </summary>
		/// <param name="date">DateTime containing the value to be set</param>
		public static void SetSystemTime(DateTime date)
		{
			DateTime gmt = date.ToUniversalTime();
			SYSTEMTIME sysTime = new SYSTEMTIME();

			sysTime.wYear = (ushort)gmt.Year;
			sysTime.wMonth = (ushort)gmt.Month;
			sysTime.wDayOfWeek = (ushort)gmt.DayOfWeek;
			sysTime.wDay = (ushort)gmt.Day;
			sysTime.wHour = (ushort)gmt.Hour;
			sysTime.wMinute = (ushort)gmt.Minute;
			sysTime.wSecond = (ushort)gmt.Second;
			sysTime.wMilliseconds = (ushort)gmt.Millisecond;

			SetSystemTime(ref sysTime);
		}
	}
}
